/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 * File Name     : roce_pd.c
 * Version       : v2.0
 * Created       : 2021/3/12
 * Last Modified : 2021/12/7
 * Description   : implement of alloc and dealloc of pd
 */

#include <linux/slab.h>

#include "roce.h"
#include "roce_main_extension.h"
#include "roce_pd.h"

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(OFED_MLNX_5_7_8) || defined(OFED_MLNX_5_4) \
    || defined(CENTOS8_2_4_18_0)
int roce3_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
    int ret;
    struct roce3_pd *pd = to_roce3_pd(ibpd);
    struct roce3_device *rdev = to_roce3_dev(ibpd->device);

    if (roce3_hca_is_present(rdev) == 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE] %s: HCA not present(return fail), func_id(%hu)\n", __func__,
            rdev->glb_func_id);
        return -EPERM;
    }

    ret = roce3_rdma_pd_alloc(rdev->hwdev, &pd->pdn);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc pdn, ret(%d), func_id(%hu)\n", __func__, ret,
            rdev->glb_func_id);
        goto err_out;
    }

    if (udata) {
        if (ib_copy_to_udata(udata, &pd->pdn, PD_RESP_SIZE) != 0) {
            ret = -EFAULT;
            dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to copy data to user space, func_id(%hu)\n", __func__,
                rdev->glb_func_id);
            goto err_copy_to_user;
        }
    }

    return 0;

err_copy_to_user:
    roce3_rdma_pd_free(rdev->hwdev, pd->pdn);
err_out:
    return ret;
}
#else
struct ib_pd *roce3_alloc_pd(struct ib_device *ibdev, struct ib_ucontext *ibcontext, struct ib_udata *udata)
{
    int ret = 0;
    struct roce3_pd *pd = NULL;
    struct roce3_device *rdev = NULL;

    if (ibdev == NULL) {
        ret = -EINVAL;
        pr_err("[ROCE, ERR] %s: Ibdev is null\n", __func__);
        goto err_out;
    }

    rdev = to_roce3_dev(ibdev);
    if (roce3_hca_is_present(rdev) == 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE] %s: HCA not present(return fail), func_id(%hu)\n", __func__,
            rdev->glb_func_id);
        ret = -EPERM;
        goto err_out;
    }

    pd = (struct roce3_pd *)kmalloc(sizeof *pd, GFP_KERNEL);
    if (pd == NULL) {
        ret = -ENOMEM;
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc memory for roce pd, func_id(%hu)\n", __func__,
            rdev->glb_func_id);
        goto err_out;
    }

    ret = roce3_rdma_pd_alloc(rdev->hwdev, &pd->pdn);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc pdn, ret(%d), func_id(%hu)\n", __func__, ret,
            rdev->glb_func_id);
        goto err_alloc_pdn;
    }

    if (ibcontext) {
        if (ib_copy_to_udata(udata, &pd->pdn, PD_RESP_SIZE) != 0) {
            ret = -EFAULT;
            dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to copy data to user space, func_id(%d)\n", __func__,
                rdev->glb_func_id);
            goto err_copy_to_user;
        }
    }

    return &pd->ibpd;

err_copy_to_user:
    roce3_rdma_pd_free(rdev->hwdev, pd->pdn);

err_alloc_pdn:
    kfree(pd);

err_out:
    return (struct ib_pd *)ERR_PTR((long)ret);
}
#endif

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(OFED_MLNX_5_7_8)
int roce3_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
#elif defined(OFED_MLNX_5_4) || defined(CENTOS8_2_4_18_0)
void roce3_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
#else
int roce3_dealloc_pd(struct ib_pd *ibpd)
#endif
{
    struct roce3_pd *pd = NULL;
    struct roce3_device *rdev = NULL;

    if (ibpd == NULL) {
        pr_err("[ROCE, ERR] %s: Ibpd is null\n", __func__);
#if !defined(OFED_MLNX_5_4) && !defined(FEATURE_NO_NETDEV_EIN_PAGE)
        return -EINVAL;
#else
        return;
#endif
    }

    pd = to_roce3_pd(ibpd);
    rdev = to_roce3_dev(ibpd->device);

    roce3_rdma_pd_free(rdev->hwdev, pd->pdn);

#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && !defined(OFED_MLNX_5_7_8) && !defined(OFED_MLNX_5_4)
    kfree(pd);
#endif

#if !defined(OFED_MLNX_5_4) && !defined(CENTOS8_2_4_18_0)
    return 0;
#endif
}
